﻿using System;
using System.Reflection;
using UnityEngine;
using System.Collections.Generic;
namespace GX
{
    public partial class UIManagerService : EventDispatcher,IUIManager
    {
        /**
         * bit[0,1] 可视状态：0不可见，1可见但不是active（非top的），2可见并且active(top)
         */
        public const int Mask_Visible = 0x03;//0011
        /**
        *  bit[2,3] 生命周期（加载/创建/销毁）状态：0为加载none，1加载中loading， 2 加载完loadDone 3：创建created
        */
        public const int Mask_Load = 0x0c;//1100

        public const int Status_InVisible = 0;
        public const int Status_Visible = 1;

        public const int Load_Status_None = 0;
        public const int Load_Status_Loading = 1;
        public const int Load_Status_Loaded = 2;
        public const int Load_Status_Create = 3;

        public const int Command_Close = 1;
        public const int Command_Open = 2;
        public const int Command_Destroy = 3;

        readonly Dictionary<string, BasePanel> _nameToPages = new Dictionary<string, BasePanel>(30);
        readonly List<BasePanel> _displayList = new List<BasePanel>(20);
        readonly Dictionary<int, int> _layerDisplayCountMap = new Dictionary<int, int>(10);

        readonly List<int> _commandTypeList = new List<int>(10);
        readonly List<string> _commandUIList = new List<string>(10);

        readonly List<Event> _argPool = new List<Event>(10);
        readonly Event _globalArgs = new Event();
        readonly Dictionary<string, Event> _argMap = new Dictionary<string, Event>(10);
        public Assembly assembly { get; set; }

        bool isLocked { get { return _commandTypeList.Count > 0; } }
        public Func<string, BasePanel> createPanelFunc { get; set; }
        public Event Args
        {
            get
            {
                _globalArgs.ClearParameters();
                return _globalArgs;
            }
        }
        public void Open(string uiName)
        {
            HandleCommand(Command_Open, uiName);
        }
        public void Close(string uiName)
        {
            HandleCommand(Command_Close, uiName);
        }
        public void Destroy(string uiName)
        {
            HandleCommand(Command_Destroy, uiName);
        }
        /**打开/关闭/销毁操作会有回调，回调过程再进行这些操作会影响逻辑
         * 所以要在队列而不是堆栈中处理
         */
        void HandleCommand(int type, string uiName)
        {
            _commandTypeList.Add(type);
            _commandUIList.Add(uiName);
            if (type == 1)
            {
                if (_globalArgs.parameterCount != 0)
                {
                    Event args = null;
                    if (_argPool.Count != 0)
                    {
                        args = _argPool[_argPool.Count - 1];
                        _argPool.RemoveAt(_argPool.Count - 1);
                    }
                    else
                        args = new Event();
                    args.CopyFrom(_globalArgs);
                    _globalArgs.ClearParameters();
                    SetArgs(uiName, args);
                }
            }
            while (_commandTypeList.Count > 0)
            {
                var command = _commandTypeList[0];
                var name = _commandUIList[0];
                switch (command)
                {
                    case Command_Open:
                        DoOpen(name);
                        break;
                    case Command_Close:
                        DoClose(name);
                        break;
                    case Command_Destroy:
                        DoDestroy(name);
                        break;
                }
                _commandTypeList.RemoveAt(0);
                _commandUIList.RemoveAt(0);
            }
        }

        void DoOpen(string uiName)
        {
            BasePanel page = null;
            if (!_nameToPages.TryGetValue(uiName, out page))
            {
                page = createPanelFunc(uiName) as BasePanel;
                if (null == page)
                {
                    Debugger.Log("未找到UI class:" + uiName, "ff0000");
                    return;
                }
                page.Initialize(this, uiName);
                _nameToPages[uiName] = page;
            }


            int len = _displayList.Count;

            BasePanel preTopPage = len != 0 ? _displayList[len - 1] : null;


            int layerStartIndex;
            int layerPageCount = GetLayerPageCount(page.layer);

            int srcIndex = -1;
            int targetIndex = -1;

            TryGetLayerStartIndex(page.layer, out layerStartIndex);


            if (!page.isOpen)//不存在：插入
            {
                page.SetVisibleStatus(Status_Visible);

                targetIndex = layerStartIndex + layerPageCount;
                _displayList.Insert(targetIndex, page);

                page.sortOrder = layerPageCount;

                _layerDisplayCountMap[page.layer] = ++layerPageCount;
            }
            else//已经存在
            {
                srcIndex = layerStartIndex + page.sortOrder;
                targetIndex = layerStartIndex + layerPageCount - 1;

                if (srcIndex == targetIndex)//已经在当前layer的top
                    return;

                //移动至所在layer的top
                for (int i = srcIndex; i < targetIndex; ++i)
                {
                    var curPage = _displayList[i + 1];
                    _displayList[i] = curPage;
                    curPage.sortOrder -= 1;
                }
                _displayList[targetIndex] = page;
                page.sortOrder = targetIndex - layerStartIndex;
            }

            len = _displayList.Count;

            bool isTop = _displayList[len - 1] == page;
            if (isTop && null != preTopPage && preTopPage != page)
            {
                preTopPage.OnPauseInternal();
            }
            //加入到显示列表
            if (-1 == srcIndex)
            {
                HandleEnter(page);
            }

            if (isTop)
            {
                page.OnResumeInternal();
            }
            page.TryCreate();
        }
        void HandleEnter(BasePanel page)
        {
            if (!page.isCreated)
                return;
            Event args = null;
            if (_argMap.TryGetValue(page.name, out args))
            {
                _argMap.Remove(page.name);
            }
            page.OnEnterInternal(args);
            if (null != args)
            {
                args.ClearParameters();
                _argPool.Add(args);
            }
        }
        void DoClose(string uiName)
        {
            BasePanel page = null;
            if (!_nameToPages.TryGetValue(uiName, out page))
                return;
            int index = _displayList.IndexOf(page);
            if (-1 == index)
                return;

            Event args = null;
            if (_argMap.TryGetValue(uiName, out args))
            {
                _argMap.Remove(uiName);
                args.ClearParameters();
                _argPool.Add(args);
            }

            _displayList.RemoveAt(index);
            page.sortOrder = -1;
            page.SetVisibleStatus(Status_InVisible);

            int layerPageCount = GetLayerPageCount(page.layer);

            --layerPageCount;

            if (0 == layerPageCount)
                _layerDisplayCountMap.Remove(page.layer);
            else
                _layerDisplayCountMap[page.layer] = layerPageCount;



            int len = _displayList.Count;

            for (int i = index; i < len; ++i)
            {
                var current = _displayList[i];
                if (current.layer == page.layer)
                {
                    current.sortOrder -= 1;
                }
                else
                    break;
            }

            page.OnExitInternal();

            if (len > 0 && index == len)
            {
                _displayList[len - 1].OnResumeInternal();
            }
        }
        void DoDestroy(string uiName)
        {
            BasePanel page = null;
            if (!_nameToPages.TryGetValue(uiName, out page))
                return;
            if (page.isOpen)
            {
                int index = _displayList.IndexOf(page);
                _displayList.RemoveAt(index);
                page.OnExitInternal();
            }
            page.OnDestroyInternal();
        }
        public bool IsOpen(string uiName)
        {
            BasePanel page = null;
            if (!_nameToPages.TryGetValue(uiName, out page))
                return false;
            return page.isOpen;
        }
        public bool IsActive(string uiName)
        {
            BasePanel page = null;
            if (!_nameToPages.TryGetValue(uiName, out page))
                return false;
            return page.isActive;
        }
        public int count { get { return _displayList.Count; } }
        internal void OnLoaded(BasePanel page)
        {
            if (!page.isOpen)
                return;

            HandleEnter(page);

            if (page.isActive)
                page.OnResumeInternal();
            else
                page.OnPauseInternal();
        }
        public BasePanel GetUIWrapper(string name)
        {
            BasePanel page = null;
            if (!_nameToPages.TryGetValue(name, out page))
                return null;
            return page;
        }
        public List<BasePanel> GetAll(List<BasePanel> retList)
        {
            retList.AddRange(_displayList);
            return retList;
        }
        public bool IsTop(string name)
        {
            return count > 0 && _displayList[count - 1].name.Equals(name);
        }
        public int GetLayerPageCount(int layer)
        {
            int value;
            if (!_layerDisplayCountMap.TryGetValue(layer, out value))
                return 0;
            return value;
        }
        public bool TryGetLayerStartIndex(int layer, out int retIndex)
        {
            int preLayer = layer - 1;
            int len = _displayList.Count;
            int low = 0, high = len - 1;
            int index = -1;
            bool gotoHigher = true;
            while (true)
            {
                if (low > high)
                    break;

                index = (low + high) / 2;

                var curPage = _displayList[index];
                gotoHigher = curPage.layer <= preLayer;

                if (gotoHigher)
                    low = index + 1;
                else
                    high = index - 1;
            }
            bool hasLayer = index != -1 && _displayList[index].layer == layer;
            if (hasLayer)
                retIndex = index;
            else
                retIndex = index + 1;
            return hasLayer;
        }
        public Event GetArgs()
        {
            _globalArgs.ClearParameters();
            return _globalArgs;
        }
        void SetArgs(string uiName, Event src)
        {
            Event args = null;
            _argMap.TryGetValue(uiName, out args);
            if (null == src)
            {
                if (null != args)
                {
                    _argMap.Remove(uiName);
                    args.ClearParameters();
                    _argPool.Add(args);
                }
                return;
            }

            if (null == args)
            {
                int len = _argPool.Count;
                if (len > 0)
                {
                    args = _argPool[len - 1];
                    _argPool.RemoveAt(len - 1);
                }
                else
                {
                    args = new Event();
                }
                _argMap[uiName] = args;
            }
            args.CopyFrom(src);
        }
    }
}